package edu.mcw.rgd.clinminer.web;

import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.themes.BaseTheme;
import com.vaadin.ui.themes.Reindeer;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;

import com.vaadin.addon.beanvalidation.BeanValidationForm;
import com.vaadin.data.Item;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.event.ShortcutAction.ModifierKey;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.ThemeResource;

import edu.mcw.rgd.clinminer.domain.Participant;
import edu.mcw.rgd.clinminer.domain.misc.UserProfile;
import edu.mcw.rgd.clinminer.security.Security;
import edu.mcw.rgd.clinminer.web.utils.NotificationErrorTop;

@Configurable
public abstract class AutomaticEntityForm<T> extends CustomComponent implements
		EntityEditor {
	private static final long serialVersionUID = 1L;
	private static final Logger log = Logger
			.getLogger(AutomaticEntityForm.class);

	private Button deleteButton;
	private Button saveButton;

	private Panel formPanel;
	private BeanValidationForm<T> form;

	@Autowired
	private ClinminerApplication application;

	@Autowired
	private Security security;

	@Override
	public void setDefaults() {
		// get user profile
		UserProfile p = UserProfile.findByName(security.getUsername());
		// exit if not the right type of form
		if (form.getField("annotatedText") == null) {
			return;
		}
		form.getField("participant").setValue(p.getDefaultParticipant());
		form.getField("documentSource").setValue(p.getDefaultDocumentSource());
		form.getField("documentSourceDate")
				.setValue(p.getDefaultDocumentDate());

	}

	@Override
	public void saveDefaults() {
		// exit if not the right type of form
		if (form.getField("annotatedText") == null) {
			return;
		}
		Participant participant = (Participant) form.getField("participant")
				.getValue();
		String documentSource = (String) form.getField("documentSource")
				.getValue();
		Date documentSourceDate = (Date) form.getField("documentSourceDate")
				.getValue();

		UserProfile p = UserProfile.findByName(security.getUsername());

		p.setDefaultDocumentDate(documentSourceDate);
		p.setDefaultDocumentSource(documentSource);
		p.setDefaultParticipant(participant);

		p.merge();
	}

	public AutomaticEntityForm(Class<T> entityClass) {
		form = new BeanValidationForm<T>(entityClass) {
			@Override
			public ErrorMessage getErrorMessage() {
				return null;
			}
		};

		form.setValidationVisible(false);
		form.setValidationVisibleOnCommit(false);

		// form editor layout
		CssLayout wrapper = new CssLayout();
		wrapper.setMargin(true);
		wrapper.setWidth("100%");
		formPanel = new Panel();
		formPanel.setIcon(new ThemeResource("img/edit-icon.png"));
		formPanel.addComponent(getForm());
		wrapper.addComponent(formPanel);

		setCompositionRoot(wrapper);

		// immediate validation but no changes to the underlying entity
		getForm().setImmediate(true);
		getForm().setWriteThrough(false);

		createFormFooter();
	}

	@Override
	public void addSaveActionListener(ClickListener listener) {
		getSaveButton().addListener(listener);
	}

	@Override
	public void addDeleteActionListener(ClickListener listener) {
		getDeleteButton().addListener(listener);
	}

	@Override
	public void setSaveAllowed(boolean canSave) {
		getSaveButton().setVisible(canSave);
		getSaveButton().setEnabled(canSave);
		getSaveButton().setClickShortcut(KeyCode.S, ModifierKey.CTRL);
		getSaveButton().setDescription("CTRL + S to save");
		// do not change the enabled state of the delete button
		getForm().getLayout().setEnabled(canSave);
	}

	@Override
	public void setDeleteAllowed(boolean canDelete) {
		getDeleteButton().setVisible(canDelete);
		getDeleteButton().setEnabled(canDelete);
	}

	@Override
	public void commit() {
		getForm().commit();
	}

	/**
	 * Override in subclass to control what is displayed on the form
	 *
	 * @param item
	 * @return collection of properties displayed on the form
	 */
	protected Collection<?> getDisplayedProperties(Item item) {
		log.debug("Available properties to display: "
				+ getItemPropertyIds(item));

		String[] props = new String[] {
				"participant",
				"annotatedText",
				"documentSource",
				"documentSourceDate",
				"directObservation",
				"eventDate",
				"exactDate",
				"LOINCLabel",
				"LOINCID",
				"ontologyTermLabel",
				"ontologyTermID",
				// Medication
				"dailyDosage",
				"doseForm",
				"days",
				// Measurement
				"value",
				"unit",
				"reference",
				// Genetic Lab
				"testedPhenotypeLabel",
				"testedPhenotypeID",
				"notes",
				// Participant
				"name",
				// Participant Details
				"firstName",
				"lastName",
				"gender",
				"DOB",
				"MRN",
				"SSN",
				// Participant Studies
				"studies",
				// Study
				"description",
				"participants",
				// Relationship
				"source",
				"relationshipType",
				"target" };
		return Arrays.asList(props);
	}

	@Override
	public void setItemDataSource(Item item) {
		if (null != item) {
			getForm().setItemDataSource(item, getDisplayedProperties(item));
		} else {
			getForm().setItemDataSource(null);
		}
	}

	/**
	 * Return item property IDs to show on the form.
	 *
	 * This method can be overridden to hide specific properties or to customize
	 * the order of the fields.
	 *
	 * @param item
	 * @return Collection of property identifiers for the item
	 */
	protected Collection<?> getItemPropertyIds(Item item) {
		if (null == item) {
			return Collections.emptyList();
		}
		return item.getItemPropertyIds();
	}

	@Override
	public Item getItemDataSource() {
		return getForm().getItemDataSource();
	}

	@Override
	public void setCommitErrorMessage(String message) {
		getApplication().getMainWindow().showNotification(
				new NotificationErrorTop("Save failed", message));
	}

	protected BeanValidationForm<T> getForm() {
		return form;
	}

	protected Button getSaveButton() {
		if (saveButton == null) {
			saveButton = new Button("Save");
			saveButton.setStyleName(Reindeer.BUTTON_DEFAULT);
		}
		return saveButton;
	}

	protected Button getDeleteButton() {
		if (deleteButton == null) {
			deleteButton = new Button("Delete");
			deleteButton.setStyleName(BaseTheme.BUTTON_LINK);
		}
		return deleteButton;
	}

	protected void createFormFooter() {
		HorizontalLayout footer = new HorizontalLayout();
		footer.setSpacing(true);
		getForm().setFooter(footer);

		footer.addComponent(getSaveButton());
		footer.addComponent(getDeleteButton());
	}

	@Override
	public void setCaption(String caption) {
		formPanel.setCaption(caption);
	}

	@Override
	public void focus() {
		getForm().focus();
	}

	@Override
	public void refresh() {
		// nothing to do
	}

	@Override
	public boolean isModified() {
		return getForm().isModified();
	}

}